﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArithmeticLibrary.Tree;
using ArithmeticLibrary.Visitors;

namespace ArithmeticLibrary
{
    /// <summary>
    /// Trida reprezentujici funkci jedne promenne.
    /// </summary>
    public class Function
    {
        #region Properties

        /// <summary>
        /// Jmeno promenne ve funkci.
        /// </summary>
        public string VariableName { get; set; }

        /// <summary>
        /// Vyraz funkce.
        /// </summary>
        public Expression Definition { get; set; }

        /// <summary>
        /// Prvni derivace funkce.
        /// </summary>
        public Expression Derivation1 { get; set; }

        /// <summary>
        /// Druha derivace funkce.
        /// </summary>
        public Expression Derivation2 { get; set; }

        #endregion // Properties

        #region Constructors

        /// <summary>
        /// Vytvori instanci funkce.
        /// </summary>
        /// <param name="expression"></param>
        public Function(Expression expression)
        {
            // priradi vyraz funkce
            Definition = expression;

            // zkontrolujeme, jestli mame funkci jedne promenne
            if (expression.Variables.Count != 1)
                throw new Exception("Nebyla zadaná funkce 1 proměnné.");

            VariableName = expression.Variables[0];

            // spocte prvni derivaci funkce
            Derivation1 = Definition.Derivate();

            // spocte druhou derivaci funkce
            Derivation2 = Derivation1.Derivate();
        }

        #endregion // Constructors

        #region Public Methods

        /// <summary>
        /// Vyhodnoti funkci v bode x.
        /// </summary>
        /// <param name="x">Bod, ve kterem se ma funkce vyhodnotit.</param>
        /// <returns>Vrati funkcni hodnotu v zadanem bode.</returns>
        public double Evaluate(double x)
        {
            double d = Definition.Evaluate(x);

            if (double.IsNaN(d) || double.IsInfinity(d))
                throw new Exception("Nedefinovaná funkční hodnota pro " + VariableName + " = " + x.ToString("f3") + ".\n"
                    + "Upravte vstupní interval (dejte pozor na definiční obor funkce).");
            else return d;
        }

        /// <summary>
        /// Vyhodnoti 1. derivaci funkce v bode x.
        /// </summary>
        /// <param name="x">Bod, ve ktere se ma 1. derivace vyhodnotit.</param>
        /// <returns>Vrati hodnotu 1. derivace funkce v zadanem bode.</returns>
        public double Evaluate1stDerivation(double x)
        {
            double d = Derivation1.Evaluate(x);

            if (double.IsNaN(d) || double.IsInfinity(d))
                throw new Exception("Neexistující 1. derivace pro " + VariableName + " = " + x.ToString("f3") + ".\n"
                    + "Upravte vstupní interval (dejte pozor na definiční obor funkce).");
            else return d;
        }

        /// <summary>
        /// Vyhodnoti 2. derivaci funkce v bode x.
        /// </summary>
        /// <param name="x">Bod, ve kterem se ma 2. derivace vyhodnotit.</param>
        /// <returns>Vrati hodnotu 2. derivace funkce v zadanem bode.</returns>
        public double Evaluate2ndDerivation(double x)
        {
            double d = Derivation2.Evaluate(x);

            if (double.IsNaN(d) || double.IsInfinity(d))
                throw new Exception("Neexistující 2. derivace pro " + VariableName + " = " + x.ToString("f3") + ".\n"
                    + "Upravte vstupní interval (dejte pozor na definiční obor funkce).");
            else return d;
        }

        /// <summary>
        /// Zderivuje funkci.
        /// </summary>
        public Expression Derivate()
        {
            return Definition.Derivate();
        }

        #endregion // Methods
    }
}
